#include <stdlib.h>
#include "../lib/cutest/CuTest.h"
#include "../include/map.h"
#include "../include/mine_map.h"
#include "../include/opt.h"

void TestOptCount(CuTest *tc) {
    for(int i=10;i<40;i++) {
        map *Map = CreateMap(i / 10); // 10 * [easy, mid, hard]
        Setmine(Map, rand() % Map -> mx + 1, rand() % Map -> my + 1, rand());
        for(int x=1;x<=Map->mx;x++)
            for(int y=1;y<=Map->my;y++) {
                int cnt = 0;
                for(int dx=-1;dx<=1;dx++)
                    for(int dy=-1;dy<=1;dy++) {
                        if(dx == 0 && dy == 0) continue;
                        int tx = x + dx, ty = y + dy;
                        if(1 <= tx && tx <= Map -> mx && 1 <= ty && ty <= Map -> my) cnt += Map -> mineMap[tx][ty];
                    }
                CuAssertIntEquals_Msg(tc, "opt_count", cnt, opt_count(Map, x, y));
            }
    }
}

void TestFnOptLeftClick(CuTest *tc) {
    for(int testcases=10;testcases<40;testcases++) {
        map *Map = CreateMap(testcases / 10); // 10 * [easy, mid, hard]
        Setmine(Map, rand() % Map -> mx + 1, rand() % Map -> my + 1, rand());
        for(int i=1;i<=20;i++) {
            int x = rand() % Map -> mx + 1, y = rand() % Map -> my + 1, preStage = Map -> stage[x][y];
            int result = fnOptLeftClick(x, y, Map);
            if(Map -> mineMap[x][y]) CuAssertIntEquals_Msg(tc, "left click boom", preStage, result);
            else CuAssertIntEquals_Msg(tc, "left click safe", BOOM, !result);
        }
    }
}

void TestFnOptRightClick(CuTest *tc) {
    for(int testcases=10;testcases<40;testcases++) {
        map *Map = CreateMap(testcases / 10); // 10 * [easy, mid, hard]
        Setmine(Map, rand() % Map -> mx + 1, rand() % Map -> my + 1, rand());
        for(int i=1;i<=20;i++) {
            int x = rand() % Map -> mx + 1, y = rand() % Map -> my + 1, preStage = Map -> stage[x][y];
            int result = fnOptRightClick(x, y, Map);
            CuAssertIntEquals_Msg(tc, "right click allowed", ALLOWED, result);
            CuAssertIntEquals_Msg(tc, "right click change stage", true, preStage != Map -> stage[x][y]);
        }
    }
}

void TestFnOptMidClick(CuTest *tc) {
    // stage -> UNCOVERED COVERED FLAGED
    // testcase definition
    map Map; 
    Map.mx = Map.my = 3;
    int testMaps[][3] = {{5, 0, 5}, {7, 0, 0}, {7, 0, 0}, {7, 0, 0}, {3, 0, 2}}; 
    int testStages[][3] = {{202, 10, 202}, {222, 10, 110}, {220, 210, 110}, {220, 10, 110}, {22, 10, 20}};
    int expectedResults[] = {ALLOWED, ALLOWED, BOOM, UNALLOWED, ALLOWED};
    int expcetedStages[][3] = {{212, 111, 212}, {222, 111, 111}, {221, 211, 111}, {220, 10, 110}, {122, 111, 121}};
    int pow10[] = {1, 10, 100};
    // test
    for(int i=0;i<5;i++) {
        for(int x=1;x<=3;x++) for(int y=1;y<=3;y++)
            Map.mineMap[x][y] = !!(testMaps[i][x-1] & (1 << (y-1))), // !!x -> (bool) x
            Map.stage[x][y] = testStages[i][x-1] / pow10[y-1] % 10,
            Map.autobook[x][y] = 0; // wtf is this
        CuAssertIntEquals_Msg(tc, "mid click result", expectedResults[i], fnOptMidClick(2, 2, &Map));
        for(int x=1;x<=3;x++) for(int y=1;y<=3;y++)
            CuAssertIntEquals_Msg(tc, "mid click stage", expcetedStages[i][x-1] / pow10[y-1] % 10, Map.stage[x][y]);
    }
}

CuSuite* OptGetSuite() {
    srand((int)time(NULL));
    CuSuite* suite = CuSuiteNew();
    SUITE_ADD_TEST(suite, TestOptCount);
    SUITE_ADD_TEST(suite, TestFnOptLeftClick);
    SUITE_ADD_TEST(suite, TestFnOptRightClick);
    SUITE_ADD_TEST(suite, TestFnOptMidClick);
    return suite;
}